package com.dongzili.common.config.aspect;

import cn.hutool.core.date.LocalDateTimeUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.dongzili.common.config.exception.GlobalExceptionHandler;
import com.dongzili.common.disruptor.entity.MessageEvent;
import com.dongzili.common.disruptor.enums.MessageType;
import com.dongzili.common.disruptor.service.DisruptorMqService;
import com.dongzili.common.vo.R;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.List;
import java.util.Objects;
import java.util.UUID;

@Aspect
@Component
@Slf4j
public class LogAspect {

    @Resource
    private TraceIdLogger messageLogger;


    @Resource
    private HttpServletRequest request;
    @Resource
    private DisruptorMqService<LogPrintInfo> disruptorMqService;

    @Pointcut("execution(* com..*Controller.*(..))")
    public void pointcut() {}

    @Around("pointcut()")
    public Object doAround(ProceedingJoinPoint joinPoint) {
        LocalDateTime start = LocalDateTime.now();

        if (messageLogger.getTraceId() == null) {
            messageLogger.setTraceId(UUID.randomUUID().toString());
        }
        String traceId = messageLogger.getTraceId();
        log.info("会话追踪ID: {}", traceId);

        Object result = null;
        Throwable throwable = null;
        try {
            result = joinPoint.proceed();
        } catch (Throwable e) {
            throwable = e;
            HttpServletRequest request = ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest();
            result = GlobalExceptionHandler.generalHandleException(new RuntimeException(e), request, messageLogger);
        } finally {
            LocalDateTime end = LocalDateTime.now();
            long duration = LocalDateTimeUtil.between(start, end, ChronoUnit.MILLIS);
            try{
                if (result instanceof R<?>) {
                    R<?> apiResponse = (R<?>) result;
                    apiResponse.setTraceId(traceId);
                    apiResponse.setTimes(duration);
                    if (apiResponse.getData() instanceof List<?>) {
                        int size = ((List<?>) apiResponse.getData()).size();
                        apiResponse.setCount((long) size);
                    } else if (apiResponse.getData() instanceof Page<?>) {
                        Page<?> page = (Page<?>) apiResponse.getData();
                        long size = page.getTotal();
                        apiResponse.setCount(size);
                    }
                }
            }catch (Exception ignore){}
            // 异步记录日志
            if (request != null) {
                String url = request.getRequestURL().toString();
                String method = request.getMethod();
                String uri = request.getRequestURI();
                String queryString = request.getQueryString();
                String ip = cn.hutool.extra.servlet.ServletUtil.getClientIP(request);
                RequestInfo requestInfo = new RequestInfo();
                requestInfo.setUrl(url);
                requestInfo.setMethod(method);
                requestInfo.setUri(uri);
                requestInfo.setQueryString(queryString);
                requestInfo.setIp(ip);
                LogPrintInfo printInfo = new LogPrintInfo();
                printInfo.setTraceId(traceId);
                printInfo.setArgs(joinPoint.getArgs());
                printInfo.setResult(result);
                printInfo.setDuration(duration);
                printInfo.setRequest(requestInfo);
                printInfo.setErrMsg(throwable==null?null:throwable.getLocalizedMessage());

                MessageEvent<LogPrintInfo> event = new MessageEvent<>();
                event.setData(printInfo);
                disruptorMqService.sendMq(MessageType.Log, event);
//                asyncLogService.logRequest(printInfo);
            }

        }

        return result;
    }
}